#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <random>
#include <chrono>
#include <vector>

using namespace std;

const int WINDOW_SIZE = 5;
const int PACKET_COUNT = 20;
const double LOSS_PROBABILITY = 0.2;

mutex mtx;
condition_variable cv;
vector<bool> packets_sent(PACKET_COUNT, false);
vector<bool> packets_acknowledged(PACKET_COUNT, false);
int send_base = 0;
int next_seq_num = 0;
int receive_base = 0;
bool running = true;

bool is_packet_lost() 
{
    random_device rd;
    mt19937 gen(rd());
    uniform_real_distribution<> dis(0.0, 1.0);
    return dis(gen) < LOSS_PROBABILITY;
}

void sender() 
{
    while (running) 
    {
        unique_lock<mutex> lock(mtx);
        if (next_seq_num < send_base + WINDOW_SIZE && next_seq_num < PACKET_COUNT) 
        {
            if (!is_packet_lost()) 
            {
                cout << "Sender: Sending packet " << next_seq_num << endl;
                packets_sent[next_seq_num] = true;
                next_seq_num++;
            } 
            else 
            {
                cout << "Sender: Packet " << next_seq_num << " lost!" << endl;
            }
        }
        cv.wait_for(lock, chrono::milliseconds(100), [&] 
        {
            return packets_acknowledged[send_base];
        });
        while (send_base < PACKET_COUNT && packets_acknowledged[send_base]) 
        {
            send_base++;
        }
    }
}

void receiver() 
{
    while (running) 
    {
        unique_lock<mutex> lock(mtx);
        for (int i = receive_base; i < receive_base + WINDOW_SIZE && i < PACKET_COUNT; ++i) 
        {
            if (packets_sent[i] && !packets_acknowledged[i]) 
            {
                if (!is_packet_lost()) 
                {
                    cout << "Receiver: Received packet " << i << endl;
                    packets_acknowledged[i] = true;
                    cv.notify_one();
                    while (receive_base < PACKET_COUNT && packets_acknowledged[receive_base]) 
                    {
                        cout << "Receiver: Delivered packet " << receive_base << " to host" << endl;
                        receive_base++;
                    }
                } 
                else 
                {
                    cout << "Receiver: Packet " << i << " lost on receive side!" << endl;
                }
            }
        }
        lock.unlock();
        this_thread::sleep_for(chrono::milliseconds(100));
    }
}

void monitor() 
{
    while (running) 
    {
        this_thread::sleep_for(chrono::seconds(1));
        unique_lock<mutex> lock(mtx);
        cout << "\nWindow Status:" << endl;
        cout << "Sent and Acknowledged: ";
        for (int i = 0; i < send_base; ++i) 
        {
            cout << i << " ";
        }
        cout << endl;

        cout << "Sent but not Acknowledged: ";
        for (int i = send_base; i < next_seq_num; ++i) 
        {
            cout << i << " ";
        }
        cout << endl;

        cout << "Allowed to send but not sent: ";
        for (int i = next_seq_num; i < send_base + WINDOW_SIZE && i < PACKET_COUNT; ++i) 
        {
            cout << i << " ";
        }
        cout << endl;

        cout << "Not allowed to send: ";
        for (int i = send_base + WINDOW_SIZE; i < PACKET_COUNT; ++i) 
        {
            cout << i << " ";
        }
        cout << endl;

        cout << "Sent ACK and delivered to host: ";
        for (int i = 0; i < receive_base; ++i) 
        {
            cout << i << " ";
        }
        cout << endl;

        cout << "Allowed to receive: ";
        for (int i = receive_base; i < receive_base + WINDOW_SIZE && i < PACKET_COUNT; ++i) 
        {
            cout << i << " ";
        }
        cout << endl;

        cout << "Not allowed to receive: ";
        for (int i = receive_base + WINDOW_SIZE; i < PACKET_COUNT; ++i) 
        {
            cout << i << " ";
        }
        cout << endl;

        int advertised_window = WINDOW_SIZE - (next_seq_num - send_base);
        int available_window = advertised_window - (receive_base - send_base);
        cout << "Advertised Window Size: " << advertised_window << endl;
        cout << "Available Window Size: " << available_window << endl;
    }
}

int main() 
{
    thread sender_thread(sender);
    thread receiver_thread(receiver);
    thread monitor_thread(monitor);

    this_thread::sleep_for(chrono::seconds(10));
    running = false;

    sender_thread.join();
    receiver_thread.join();
    monitor_thread.join();

    return 0;
}
    